﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Microsoft.Office.Interop.Outlook;
using Outlook.WorkItems.Extension.Manager.TeamProjectManager;
using Outlook.WorkItems.Extension.Managers;
using Office = Microsoft.Office.Core;
using Outlook.WorkItems.Extension.Controls;
using Outlook.WorkItems.Extension.Addin.Helpers;
using Outlook.WorkItems.Extension.Addin.Properties;
using Outlook.WorkItems.Extension.Manager.TeamBuildManager;

namespace Outlook.WorkItems.Extension.Addin
{
    public partial class ThisAddIn
    {
        private Application _application;
        internal static Office.IRibbonUI _ribbon;
        private Explorers _explorers;
        private Inspectors _inspectors;
        public stdole.IPictureDisp _pictdisp;
        // List of tracked explorer windows         
        internal static List<OutlookExplorer> _windows;
        // List of tracked inspector windows         
        internal static List<OutlookInspector> _inspectorWindows;
        private List<MAPIFolder> _folders;
        private readonly List<Items> _foldersItems = new List<Items>();

        //[PreEmptive.Attributes.Setup(CustomEndpoint = "so-s.info/PreEmptive.Web.Services.Messaging/MessagingServiceV2.asmx")]
        private void ThisAddIn_Startup(object sender, EventArgs e)
        {
            _application = Application;
            _explorers = _application.Explorers;
            _inspectors = _application.Inspectors;
            _windows = new List<OutlookExplorer>();
            _inspectorWindows = new List<OutlookInspector>();

            // Wire up event handlers to handle multiple Explorer windows
            _explorers.NewExplorer += m_Explorers_NewExplorer;
            // Wire up event handlers to handle multiple Inspector windows
            _inspectors.NewInspector += m_Inspectors_NewInspector;
            // Add the ActiveExplorer to _windows
            var expl = _application.ActiveExplorer();
            var window = new OutlookExplorer(expl);
            _windows.Add(window);
            // Hook up event handlers for window
            window.Close += WrappedWindow_Close;
            window.InvalidateControl += WrappedWindow_InvalidateControl;

            _folders = new List<MAPIFolder>();
            InitFoldersEvent(null);
            InitCalendarFolder(null);
        }

        //[PreEmptive.Attributes.Teardown()]
        private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
        {
            // Shutdown code here
            // Unhook event handlers
            _explorers.NewExplorer -= m_Explorers_NewExplorer;
            _inspectors.NewInspector -= m_Inspectors_NewInspector;

            try
            {
                _foldersItems.Clear();
                _folders.Clear();
                _folders = null;
                foreach (var item in TeamProjectLinksManager.GetAllTeamProjectLink())
                {
                    try
                    {
                        var folder = Application.Session.GetFolderFromID(item.FolderEntryId, item.FolderStoreId);
                        folder.Items.ItemAdd -= Items_ItemAdd;
                    }
                    catch
                    { }
                }
            }
            catch { }

            // Dereference objects
            _pictdisp = null;
            _explorers = null;
            _inspectors = null;
            _windows.Clear();
            _windows = null;
            _inspectorWindows.Clear();
            _inspectorWindows = null;
            _ribbon = null;
            _application = null;
        }

        #region VSTO generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InternalStartup()
        {
            Startup += ThisAddIn_Startup;
            Shutdown += ThisAddIn_Shutdown;
        }

        protected override Office.IRibbonExtensibility CreateRibbonExtensibilityObject()
        {
            return new BowieRibbonExtensibility(_application);
        }

        #endregion

        /// <summary>
        /// Looks up the window wrapper for a given window object
        /// </summary>
        /// <param name="window">An outlook explorer window</param>
        /// <returns></returns>
        internal static OutlookExplorer FindOutlookExplorer(object window)
        {
            return _windows.FirstOrDefault(explorer => explorer.Window == window);
        }

        /// <summary>
        /// Looks up the window wrapper for a given window object
        /// </summary>
        /// <param name="window">An outlook inspector window</param>
        /// <returns></returns>
        internal static OutlookInspector FindOutlookInspector(object window)
        {
            return _inspectorWindows.FirstOrDefault(inspector => inspector.Window == window);
        }

        /// <summary>
        /// The NewExplorer event fires whenever a new Explorer is displayed. 
        /// </summary>
        /// <param name="explorer"></param>
        private void m_Explorers_NewExplorer(Explorer explorer)
        {
            try
            {
                // Check to see if this is a new window 
                // we don't already track
                var existingWindow = FindOutlookExplorer(explorer);

                // If the _windows collection does not 
                // have a window for this Explorer,
                // we should add it to _windows
                if (existingWindow == null)
                {
                    var window = new OutlookExplorer(explorer);
                    window.Close += WrappedWindow_Close;
                    window.InvalidateControl += WrappedWindow_InvalidateControl;
                    _windows.Add(window);
                }
            }
            catch (System.Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
        }

        /// <summary>
        /// The NewInspector event fires whenever a new Inspector is displayed. 
        /// </summary>
        /// <param name="inspector"></param>
        void m_Inspectors_NewInspector(Inspector inspector)
        {
            _ribbon.Invalidate();

            try
            {
                // Check to see if this is a new window 
                // we don't already track
                var existingWindow = FindOutlookInspector(inspector);
                // If the _inspectorWindows collection does not 
                // have a window for this Inspector,
                // we should add it to _inspectorWindows
                if (existingWindow == null)
                {
                    var window = new OutlookInspector(inspector);
                    window.Close += WrappedInspectorWindow_Close;
                    window.InvalidateControl += WrappedInspectorWindow_InvalidateControl;
                    _inspectorWindows.Add(window);
                }
            }
            catch (System.Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
        }

        void WrappedInspectorWindow_InvalidateControl(object sender, OutlookInspector.InvalidateEventArgs e)
        {
            if (_ribbon != null)
            {
                _ribbon.InvalidateControl(e.ControlId);
            }
        }

        void WrappedInspectorWindow_Close(object sender, EventArgs e)
        {
            var window = (OutlookInspector)sender;
            window.Close -= WrappedInspectorWindow_Close;
            _inspectorWindows.Remove(window);
        }

        void WrappedWindow_InvalidateControl(object sender, OutlookExplorer.InvalidateEventArgs e)
        {
            if (_ribbon != null)
            {
                _ribbon.InvalidateControl(e.ControlId);
                _ribbon.InvalidateControl("trackInWorkItemExplorerMailButton");
                _ribbon.InvalidateControl("relatedWorkItemExplorerMailButton");
                _ribbon.InvalidateControl("sendAttachmentsExplorerMailButton");
            }
        }

        void WrappedWindow_Close(object sender, EventArgs e)
        {
            var window = (OutlookExplorer)sender;
            window.Close -= WrappedWindow_Close;
            _windows.Remove(window);
        }

        /// <summary>
        /// ItemAdd Event Manager
        /// When an item is added to a folder linking to a specific Team Project, actions that was configure are executed
        /// </summary>
        /// <param name="item"></param>
        private void Items_ItemAdd(object item)
        {
            try
            {
                if (item is MailItem)
                    ThreadPool.QueueUserWorkItem(DragDropMailItemAction, item);
            }
            catch (System.Exception ex)
            {
                Trace.WriteLine(string.Format("Exception in  : Items_ItemAdd - Message : {0}", ex.Message));
                using (var frm = new ErrorForm(ex))
                {
                    frm.ShowDialog();
                }
            }
        }

        private void DragDropMailItemAction(object state)
        {
            var mail = state as MailItem;
            if (mail == null)
                return;

            var folder = (MAPIFolder)mail.Parent;

            if (!TeamProjectLinksManager.ContainsTeamProjectLink(folder.EntryID))
                return;

            var link = TeamProjectLinksManager.GetTeamProjectLink(folder.EntryID);
            if (link.CreateWiAction != null)
            {
                link.CreateWiAction.DoAction(mail);
            }
            if (link.UploadAttachmentsAction != null)
            {
                link.UploadAttachmentsAction.DoAction(mail);
            }
        }

        public void InitCalendarFolder(object userState)
        {
            foreach (var item in TeamBuildLinksManager.GetAllBuildCalendarLink())
            {
                var folder = Application.Session.GetFolderFromID(item.FolderEntryID, item.FolderStoreID);
                if (folder == null)
                    return;

                folder.SetCustomIcon(ConvertImage.ConvertToStdPicture(Resources.LinkProject));
            }
        }

        public void InitFoldersEvent(object userState)
        {
            foreach (var item in TeamProjectLinksManager.GetAllTeamProjectLink())
            {
                MAPIFolder folder = null;
                if (_folders.Exists(f => f.EntryID == item.FolderEntryId))
                    continue;

                folder = Application.Session.GetFolderFromID(item.FolderEntryId, item.FolderStoreId);

                _folders.Add(folder);
                try
                {
                    var nFolder = (Folder)folder;
                    nFolder.SetCustomIcon(ConvertImage.ConvertToStdPicture(Resources.LinkProject));
                }
                catch { }

                try
                {
                    folder.Items.ItemAdd -= Items_ItemAdd;
                }
                catch { }
                try
                {
                    var items = folder.Items;
                    _foldersItems.Add(items);
                    items.ItemAdd += Items_ItemAdd;
                    Trace.WriteLine(string.Format("Abonnement à l'événement ItemAdd du répertoire {0} réussi", folder.Name));
                }
                catch (System.Exception ex)
                {
                    Trace.WriteLine(string.Format("Exception in InitFoldersEvent - Abonnement à l'événement ItemAdd - Message : {0}", ex.Message));
                }
            }
        }
    }

    /// <summary>
    /// This class tracks the state of an Outlook Explorer window for your
    /// add-in and ensures that what happens in this window is handled correctly.
    /// </summary>
    class OutlookExplorer
    {
        #region Instance Variables

        #endregion


        #region Events

        public event EventHandler Close;
        public event EventHandler<InvalidateEventArgs> InvalidateControl;

        #endregion


        #region Constructor

        /// <summary>
        /// Create a new instance of the tracking class for a particular explorer 
        /// </summary>
        /// <param name="explorer">A new explorer window to track</param>
        ///<remarks></remarks>
        public OutlookExplorer(Explorer explorer)
        {
            Window = explorer;

            // Hookup Close event
            ((ExplorerEvents_Event)explorer).Close += OutlookExplorerWindow_Close;

            // Hookup SelectionChange event
            Window.SelectionChange += m_Window_SelectionChange;
        }

        #endregion

        #region Event Handlers

        /// <summary>
        /// Event Handler for SelectionChange event
        /// </summary>
        private void m_Window_SelectionChange()
        {
            RaiseInvalidateControl("readMailTab");
        }

        /// <summary>
        /// Event Handler for Close event.
        /// </summary>
        private void OutlookExplorerWindow_Close()
        {
            // Unhook explorer-level events

            Window.SelectionChange -=
                new ExplorerEvents_10_SelectionChangeEventHandler(
                m_Window_SelectionChange);

            ((ExplorerEvents_Event)Window).Close -=
                new ExplorerEvents_CloseEventHandler(
                OutlookExplorerWindow_Close);

            // Raise the OutlookExplorer close event
            if (Close != null)
            {
                Close(this, EventArgs.Empty);
            }

            Window = null;
        }

        #endregion

        #region Methods
        private void RaiseInvalidateControl(string controlId)
        {
            if (InvalidateControl != null)
                InvalidateControl(this, new InvalidateEventArgs(controlId));
        }
        #endregion

        #region Properties

        /// <summary>
        /// The actual Outlook explorer window wrapped by this instance
        /// </summary>
        internal Explorer Window { get; private set; }

        #endregion

        #region Helper Class
        public class InvalidateEventArgs : EventArgs
        {
            public string ControlId { get; private set; }

            public InvalidateEventArgs(string controlId)
            {
                ControlId = controlId;
            }
        }
        #endregion
    }

    /// <summary>
    /// This class tracks the state of an Outlook Inspector window for your
    /// add-in and ensures that what happens in this window is handled correctly.
    /// </summary>
    class OutlookInspector
    {
        #region Instance Variables

        // Use these instance variables to handle item-level events
        private MailItem _mail;                // wrapped MailItem
        private AppointmentItem _appointment;  // wrapped AppointmentItem
        private ContactItem m_Contact;          // wrapped ContactItem
        private ContactItem _task;             // wrapped TaskItem
        // Define other class-level item instance variables as needed

        #endregion

        #region Events

        public event EventHandler Close;
        public event EventHandler<InvalidateEventArgs> InvalidateControl;

        #endregion

        #region Constructor

        /// <summary>
        /// Create a new instance of the tracking class for a particular 
        /// inspector and custom task pane.
        /// </summary>
        /// <param name="inspector">A new inspector window to track</param>
        ///<remarks></remarks>
        public OutlookInspector(Inspector inspector)
        {
            Window = inspector;

            // Hookup the close event
            ((InspectorEvents_Event)inspector).Close += OutlookInspectorWindow_Close;

            // Hookup item-level events as needed
            // For example, the following code hooks up PropertyChange
            // event for a ContactItem
            //OutlookItem olItem = new OutlookItem(inspector.CurrentItem);
            //if(olItem.Class==Outlook.OlObjectClass.olContact)
            //{
            //    m_Contact = olItem.InnerObject as Outlook.ContactItem;
            //    m_Contact.PropertyChange +=
            //        new Outlook.ItemEvents_10_PropertyChangeEventHandler(
            //        m_Contact_PropertyChange);
            //}

        }
        #endregion

        #region Event Handlers

        /// <summary>
        /// Event Handler for the inspector close event.
        /// </summary>
        private void OutlookInspectorWindow_Close()
        {
            // Unhook events from any item-level instance variables
            //m_Contact.PropertyChange -= 
            //    Outlook.ItemEvents_10_PropertyChangeEventHandler(
            //    m_Contact_PropertyChange);

            // Unhook events from the window
            ((InspectorEvents_Event)Window).Close -= OutlookInspectorWindow_Close;

            // Raise the OutlookInspector close event
            if (Close != null)
            {
                Close(this, EventArgs.Empty);
            }

            // Unhook any item-level instance variables
            //m_Contact = null;
            Window = null;
        }


        //void  m_Contact_PropertyChange(string Name)
        //{
        //    // Implement PropertyChange here
        //}
        #endregion

        #region Methods
        private void RaiseInvalidateControl(string controlId)
        {
            if (InvalidateControl != null)
                InvalidateControl(this, new InvalidateEventArgs(controlId));
        }
        #endregion

        #region Properties

        /// <summary>
        /// The actual Outlook inspector window wrapped by this instance
        /// </summary>
        internal Inspector Window { get; private set; }

        #endregion

        #region Helper Class

        public class InvalidateEventArgs : EventArgs
        {
            public string ControlId { get; private set; }

            public InvalidateEventArgs(string controlId)
            {
                ControlId = controlId;
            }


        }
        #endregion
    }
}
